/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * Description: Hmfs-tools main area
 */

#include "compare_main_area.h"

#include <memory>
#include <cinttypes>
#include <cmath>
#include <string>
#include <sys/types.h>

#include "hmfs_log.h"
#include "hmfs_utils.h"
#include "io.h"
#include "securec.h"

namespace OHOS {
namespace Hmfs {
int32_t CompareMainArea::CompareMainAreaBinary()
{
    srcInfo_.mainBlockBuf = std::make_unique<char[]>(HMFS_BLOCK_SIZE);
    dstInfo_.mainBlockBuf = std::make_unique<char[]>(HMFS_BLOCK_SIZE);

    if (CompareRootInode()) {
        return -1;
    }

    for (uint32_t i = 0; i < srcInfo_.quotaCount; i++) {
        if (CompareQfInode()) {
            HMFS_ERROR("Quota inode not equal, i = %u", i);
            return -1;
        }
    }

    if ((srcInfo_.features & HMFS_FEATURE_LOST_FOUND) && CompareLpfInode()) {
        return -1;
    }

    if (CompareDefaultDentryBlock()) {
        return -1;
    }

    for (int32_t i = 0; i < srcInfo_.quotaCount; i++) {
        if (CompareQuotaData()) {
            HMFS_ERROR("Quota Data not equal, i = %u", i);
            return -1;
        }
    }

    if ((srcInfo_.features & HMFS_FEATURE_LOST_FOUND) && CompareLpfDentryBlock()) {
        return -1;
    }

    return 0;
}

int32_t CompareMainArea::CompareRootInode()
{
    uint64_t blockId = GetLeValue(srcInfo_.superBlock->mainBlockId) + srcInfo_.curSeg[CURSEG_NODE_HOT] * BLOCKS_PER_SEGMENT;
    HMFS_INFO("Root Inode blockId = 0x%" PRIx64 "", blockId);

    if (DevRead(srcInfo_, srcInfo_.mainBlockBuf.get(), blockId * HMFS_BLOCK_SIZE, HMFS_BLOCK_SIZE)) {
        HMFS_ERROR("Failed to read main area.");
        return -1;
    }

    if (DevRead(dstInfo_, dstInfo_.mainBlockBuf.get(), blockId * HMFS_BLOCK_SIZE, HMFS_BLOCK_SIZE)) {
        HMFS_ERROR("Failed to read main area.");
        return -1;
    }

    NodeData* srcNodeData = reinterpret_cast<NodeData*>(srcInfo_.mainBlockBuf.get());
    NodeData* dstNodeData = reinterpret_cast<NodeData*>(dstInfo_.mainBlockBuf.get());

    if (CompareNodeDataInode(srcNodeData, dstNodeData)) {
        HMFS_ERROR("Root Inode not equal, at blockId 0x%" PRIx64 "", blockId);
        return -1;
    }

    currentBlkId_ = blockId + 1;
    return 0;
}

int32_t CompareMainArea::CompareQfInode()
{
    uint64_t blockId = currentBlkId_;
    HMFS_INFO("Qf Inode blockId = 0x%" PRIx64 "", blockId);

    if (DevRead(srcInfo_, srcInfo_.mainBlockBuf.get(), blockId * HMFS_BLOCK_SIZE, HMFS_BLOCK_SIZE)) {
        HMFS_ERROR("Failed to read main area.");
        return -1;
    }

    if (DevRead(dstInfo_, dstInfo_.mainBlockBuf.get(), blockId * HMFS_BLOCK_SIZE, HMFS_BLOCK_SIZE)) {
        HMFS_ERROR("Failed to read main area.");
        return -1;
    }

    NodeData* srcNodeData = reinterpret_cast<NodeData*>(srcInfo_.mainBlockBuf.get());
    NodeData* dstNodeData = reinterpret_cast<NodeData*>(dstInfo_.mainBlockBuf.get());

    if (CompareNodeDataInode(srcNodeData, dstNodeData)) {
        HMFS_ERROR("Qf Inode not equal, at blockId 0x%" PRIx64 "", blockId);
        return -1;
    }

    currentBlkId_ = blockId + 1;
    return 0;
}

int32_t CompareMainArea::CompareLpfInode()
{
    uint64_t blockId = currentBlkId_;
    HMFS_INFO("Lpf Inode blockId = 0x%" PRIx64 "", blockId);

    if (DevRead(srcInfo_, srcInfo_.mainBlockBuf.get(), blockId * HMFS_BLOCK_SIZE, HMFS_BLOCK_SIZE)) {
        HMFS_ERROR("Failed to read main area.");
        return -1;
    }

    if (DevRead(dstInfo_, dstInfo_.mainBlockBuf.get(), blockId * HMFS_BLOCK_SIZE, HMFS_BLOCK_SIZE)) {
        HMFS_ERROR("Failed to read main area.");
        return -1;
    }

    NodeData* srcNodeData = reinterpret_cast<NodeData*>(srcInfo_.mainBlockBuf.get());
    NodeData* dstNodeData = reinterpret_cast<NodeData*>(dstInfo_.mainBlockBuf.get());

    if (CompareNodeDataInode(srcNodeData, dstNodeData)) {
        HMFS_ERROR("Lpf Inode not equal, at blockId 0x%" PRIx64 "", blockId);
        return -1;
    }

    currentBlkId_ = blockId + 1;
    return 0;
}

int32_t CompareMainArea::CompareNodeDataInode(NodeData *srcNodeData, NodeData *dstNodeData)
{
    COMPARE_NUMBER(srcNodeData, dstNodeData, footer.nodeId);
    COMPARE_NUMBER(srcNodeData, dstNodeData, footer.inodeNumber);
    COMPARE_NUMBER(srcNodeData, dstNodeData, footer.flag);
    COMPARE_NUMBER(srcNodeData, dstNodeData, footer.checkPointVersion);
    COMPARE_NUMBER(srcNodeData, dstNodeData, footer.nextBlockAddr);

    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.fileMode);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.fileHints);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.inlineFlags);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.userId);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.groupId);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.linkCount);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.fileSize);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.blockSize);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.fileVersion);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.directoryDepth);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.gcFailureCount);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.xattrNodeId);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.fileAttributes);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.parentInodeNumber);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.fileNameLength);
    COMPARE_STRING(srcNodeData, dstNodeData, inode.fileName);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.directoryLevel);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.largestExtent.fileOffset);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.largestExtent.blkAddr);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.largestExtent.len);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.extraInodeSize);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.inlineXattrSize);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.projectId);
    COMPARE_NUMBER_NORETURN(srcNodeData, dstNodeData, inode.inodeChecksum);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.compressedBlockCount);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.compressionAlgorithm);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.logClusterSize);
    COMPARE_NUMBER(srcNodeData, dstNodeData, inode.padding);
    for (auto i = 0; i < 5; i++) {
        COMPARE_NUMBER(srcNodeData, dstNodeData, inode.nodeIds[i], i);
    }

    return 0;
}

int32_t CompareMainArea::CompareDefaultDentryBlock()
{
    uint64_t blockId = GetLeValue(srcInfo_.superBlock->mainBlockId) +
        srcInfo_.curSeg[CURSEG_DATA_HOT] * BLOCKS_PER_SEGMENT;
    HMFS_INFO("Default dentryBlock blockId = 0x%" PRIx64 "", blockId);

    if (DevRead(srcInfo_, srcInfo_.mainBlockBuf.get(), blockId * HMFS_BLOCK_SIZE, HMFS_BLOCK_SIZE)) {
        HMFS_ERROR("Failed to read main area.");
        return -1;
    }

    if (DevRead(dstInfo_, dstInfo_.mainBlockBuf.get(), blockId * HMFS_BLOCK_SIZE, HMFS_BLOCK_SIZE)) {
        HMFS_ERROR("Failed to read main area.");
        return -1;
    }

    DentryBlock* srcDentryBlock = reinterpret_cast<DentryBlock*>(srcInfo_.mainBlockBuf.get());
    DentryBlock* dstDentryBlock = reinterpret_cast<DentryBlock*>(dstInfo_.mainBlockBuf.get());

    if (CompareDentryBlock(srcDentryBlock, dstDentryBlock)) {
        HMFS_ERROR("Default dentry block not equal, at blockId 0x%" PRIx64 "", blockId);
        return -1;
    }

    currentBlkId_ = blockId + 1;
    return 0;
}

int32_t CompareMainArea::CompareLpfDentryBlock()
{
    uint64_t blockId = currentBlkId_;
    HMFS_INFO("Lpf dentryBlock blockId = 0x%" PRIx64 "", blockId);

    if (DevRead(srcInfo_, srcInfo_.mainBlockBuf.get(), blockId * HMFS_BLOCK_SIZE, HMFS_BLOCK_SIZE)) {
        HMFS_ERROR("Failed to read main area.");
        return -1;
    }

    if (DevRead(dstInfo_, dstInfo_.mainBlockBuf.get(), blockId * HMFS_BLOCK_SIZE, HMFS_BLOCK_SIZE)) {
        HMFS_ERROR("Failed to read main area.");
        return -1;
    }

    DentryBlock* srcDentryBlock = reinterpret_cast<DentryBlock*>(srcInfo_.mainBlockBuf.get());
    DentryBlock* dstDentryBlock = reinterpret_cast<DentryBlock*>(dstInfo_.mainBlockBuf.get());

    if (CompareDentryBlock(srcDentryBlock, dstDentryBlock)) {
        HMFS_ERROR("Lpf dentry block not equal, at blockId 0x%" PRIx64 "", blockId);
        return -1;
    }

    currentBlkId_ = blockId + 1;
    return 0;
}

int32_t CompareMainArea::CompareDentryBlock(DentryBlock *srcDentryBlock, DentryBlock *dstDentryBlock)
{
    for (uint32_t i = 0; i < DENTRY_BITMAP_SIZE; i++) {
        COMPARE_NUMBER(srcDentryBlock, dstDentryBlock, dentryBitmap[i], i);
    }

    for (uint32_t i = 0; i < RESERVED_SIZE; i++) {
        COMPARE_NUMBER(srcDentryBlock, dstDentryBlock, reserved[i], i);
    }

    for (uint32_t i = 0; i < DENTRY_COUNT_IN_BLOCK; i++) {
        COMPARE_STRING(srcDentryBlock, dstDentryBlock, filename[i], i);
    }

    return 0;
}

int32_t CompareMainArea::CompareQuotaData()
{
    for (uint32_t i = 0; i < QUOTA_DATA_BLOCK_COUNT; i++) {
        uint64_t blockId = currentBlkId_;
        HMFS_INFO("Quota data blockId = 0x%" PRIx64 "", blockId);

        if (DevRead(srcInfo_, srcInfo_.mainBlockBuf.get(), blockId * HMFS_BLOCK_SIZE, HMFS_BLOCK_SIZE)) {
            HMFS_ERROR("Failed to read main area.");
            return -1;
        }

        if (DevRead(dstInfo_, dstInfo_.mainBlockBuf.get(), blockId * HMFS_BLOCK_SIZE, HMFS_BLOCK_SIZE)) {
            HMFS_ERROR("Failed to read main area.");
            return -1;
        }

        if (memcmp(srcInfo_.mainBlockBuf.get(), dstInfo_.mainBlockBuf.get(), HMFS_BLOCK_SIZE)) {
            HMFS_ERROR("Quota data not equal, at blockId 0x%" PRIx64 "", blockId);
            return -1;
        }

        currentBlkId_++;
    }
    return 0;
}
} // namespace Hmfs
} // namespace OHOS
